using System.Collections.Generic;
using UnityEngine;
using UnityEditor;
using System;
using System.Linq;
using Sailfish;
using System.Reflection;
using System.Text;
using System.IO;
using UnityEngine.UIElements;

public class UIActionModuleEditor : Editor
{
    const string saveFuncCs = "Scripts/Auto/CallDefine";
    static List<PanelData.SimpleRecord> simpleRecords = null;
    static UIActionModuleData record = null;

 

    /// <summary>
    /// 保存最新修改的时间戳
    /// </summary>
    static void SaveCurrentTime()
    {
        record.saveDate = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
    }


    static bool CheckCycleTime()
    {
        try
        {
            var date = DateTime.Parse(record.saveDate);
            var now = DateTime.Now;

            TimeSpan timeSpan = now - date;

            if (timeSpan.Seconds < 10)
            {
                return true;
            }
        }
        catch 
        {
            return true;
        }
        return false;
    }

    static List<Type> customAttrs = new List<Type>()
    {   
        typeof(UIAction),
    };

    [UnityEditor.Callbacks.DidReloadScripts]
    public  static void AllScriptsReloaded()
    {
        var types = AppDomain.CurrentDomain.GetAssemblies()
                   .SelectMany(m => m.GetTypes().Where(t => t.IsSubclassOf(typeof(UISamplePanel))))
                   .ToList();


        record        = UIActionModuleData.instance;
        simpleRecords = new List<PanelData.SimpleRecord>();

        if (CheckCycleTime())
        {
            if (record == null)
            {
                return;
            }
        }

        bool isHas = false;

        foreach (var item in types)
        {
            PanelData data = null;
            simpleRecords.Clear();

            if (record.datas.Count > 0)
            {
                foreach (var s_data in record.datas)
                {
                    if (s_data.scriptCS == item.Name)
                    {
                        data = s_data;
                        break;
                    }
                }
            }

            if (data == null)
            {
                data = CreatPanel(item, record.datas);
            }


            var methods  = item.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.DeclaredOnly);
            var isMethod = FindMethod(data, methods);


            if (isMethod)
            {
                isHas = true;

                CheckState(data);

                var creat = new FuncIDEditorClass(data, 0);
                var cs    = $"{Application.dataPath}/{saveFuncCs}/{data.scriptCS}(CallID).cs";

                var parentStr = Path.GetDirectoryName(cs);
                if (!Directory.Exists(parentStr))
                {
                    Directory.CreateDirectory(parentStr);
                }
                File.WriteAllText(cs, creat.builder.ToString());
            }
        }

        if (isHas)
        {
            SaveCurrentTime();
            AssetDatabase.SaveAssets();
            EditorUtility.SetDirty(record);
        }  
    }



    private static bool FindMethod(PanelData data, MethodInfo[] methods)
    {
        bool isHas = false;
        foreach (var method in methods)
        {
            foreach (var s_type in customAttrs)
            {
                var iMethod = method.GetCustomAttribute(s_type, true);
                if (iMethod != null)
                {
                    if (AddData(data, method.Name, iMethod))
                    {
                        isHas = true;
                        break;
                    }
                }
            }
        }
        return isHas;
    }

    static bool AddData(PanelData data, string name, Attribute iMethod)
    {
        var isHas = false;
        var cmd   = ((IMethod)iMethod).Cmd;

        if (string.IsNullOrEmpty(cmd))
        {
            cmd = name;
        }

        simpleRecords.Add(new PanelData.SimpleRecord() { cmd = cmd, main = name });

        //�ж��Ƿ����ظ���Call �ֶ�
        if (TryGetCalls(data, cmd, out var findDatas) == false)
        {
            //��ȡ�̶��ֶΣ������ֶΣ�������,���ھ͸���Call�ֶ�
            if (TryGetFixed(data, name, out var s_data))
            {
                //���Ҿɵ�Call���ݣ�����о͸���״̬
                if (TryGetCalls(data, s_data.callArg, out var oldData))
                {
                    foreach (var old in oldData)
                    {
                        old.newFixedArg = name;
                        old.callArg  = cmd;
                    }
                }

                //��ΪCall �ֶβ�ͬ�ˣ���������ʧЧ
                s_data.fixedArg = name;
                s_data.callArg  = cmd;   
            }
            else
            {  
                 //�����µ�����
                s_data.callArg  = cmd;
                s_data.fixedArg = name;
                data.args.Add(s_data);
            }

            isHas = true;
        }
        else
        {
            //�ж�Call �� fixed�ֶ��Ƿ���ͬ
            foreach (var item in findDatas)
            {
                if (item.fixedArg != name)
                {
                    //�ж���û��name�Ĺ̶��ֶ�
                    if (TryGetFixed(data, name, out var s_data) == false)
                    {
                        s_data.callArg  = cmd;
                        s_data.fixedArg = name;
                        data.args.Add(s_data);
                        isHas = true;
                    }

                    item.newFixedArg = name;
                }
              
            }
        }
      
        return isHas;
    }


    static bool TryGetFixed(PanelData panel, string name, out PanelData.ArgsData data)
    {
        var s_datas = panel.args.Where(_ => _.fixedArg == name).ToList();
        if (s_datas.Count > 0)
        {
            data = s_datas[0];
            return true;
        }

        data = new PanelData.ArgsData();
        return false;
    }


    static bool TryGetCalls(PanelData panel, string name, out List<PanelData.ArgsData> datas)
    {
        var s_datas = panel.args.Where(_ => _.callArg == name).ToList();
        if (s_datas.Count > 0)
        {
            datas = s_datas;
            return true;
        }
        datas = new List<PanelData.ArgsData>();
        return false;
    }


    static void CheckState(PanelData data)
    {
        foreach (var item in data.args)
        {
            bool isNotCall  = false;
            bool isNotFixed = false;

            isNotCall  = simpleRecords.Where(_ => _.cmd  == item.callArg).Count() == 0;
            isNotFixed = simpleRecords.Where(_ => _.main == item.fixedArg).Count() == 0;

            if (isNotCall && isNotFixed)
            {
                item.obsolete = Obsolete.failure;
            }
            else if (isNotCall || isNotFixed)
            { 
                item.obsolete = Obsolete.Expired;
            }
            else
            {
                item.obsolete = Obsolete.None;
            }
        }
    }

    static PanelData CreatPanel(Type item, List<PanelData> datas)
    {
        PanelData data = new PanelData();   
        data.scriptCS = item.Name;
        datas.Add(data);
        return data;
    }



    [CustomPropertyDrawer(typeof(PanelData.ArgsData))]
    public class CustomArg : PropertyDrawer
    {
        public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
        {
            var obsolete    = property.FindPropertyRelative("obsolete");
            var cmd         = property.FindPropertyRelative("callArg");
            var fixedArg    = property.FindPropertyRelative("fixedArg");
            var newFixedArg = property.FindPropertyRelative("newFixedArg");


            if (obsolete.enumValueIndex == 0)
            {
                GUI.Label(GetLast(position), $"{fixedArg.stringValue}   =>");
                GUI.Label(GetNext(position), $"Event Key [{cmd.stringValue}]");
            }
            else if(obsolete.enumValueIndex == 1)
            {
                var color = GUI.skin.label.normal.textColor;
                GUI.skin.label.normal.textColor = Color.yellow;
                GUI.Label((position), $"{fixedArg.stringValue}  is expired, Use the latest values  =>  {newFixedArg.stringValue}");
                GUI.skin.label.normal.textColor = color;
            }
            else if (obsolete.enumValueIndex == 2)
            {
                var color = GUI.skin.label.normal.textColor;
                GUI.skin.label.normal.textColor = Color.gray;
                GUI.Label((position), $"{fixedArg.stringValue}  is invalid, Use the latest values => {newFixedArg.stringValue} ");
                GUI.skin.label.normal.textColor = color;
            }
        }
        public Rect GetLast(Rect current)
        {
            return new Rect(current.x , current.y, current.width / 2, current.height);
        }

        public Rect GetNext(Rect current)
        {
            return new Rect(current.x + current.width / 2, current.y, current.width / 2, current.height);
        }

        public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
        {
            return 20;
        }
    }


    public  class FuncIDEditorClass
    {
        public StringBuilder builder = new StringBuilder();

        public FuncIDEditorClass(PanelData autoData, int order)
        {
            var className = CreatUtil.GetClassString("UICallID");

            builder.Clear();
            builder.AppendLine($"  {CreatUtil.GetFormat(order)}//�Զ������࣬�����޸�");
            builder.AppendLine($"  {CreatUtil.GetFormat(order)}public partial class {className}");
            builder.AppendLine($"  {CreatUtil.GetFormat(order)}{CreatUtil.LeftSymbol}");


            var tag = autoData.scriptCS.Replace("Panel", "").Replace("Window", "");

            builder.AppendLine($"  {CreatUtil.GetFormat(order + 2)}{($"//------------------------{autoData.scriptCS}--------------------")}");
            foreach (var method in autoData.args)
            {
                if (method.obsolete != Obsolete.None)
                {
                    builder.AppendLine($"   {CreatUtil.GetFormat(order + 3)}  [System.Obsolete(\"{GetObsoleteString(method,autoData)}\")] ");
                }
                builder.AppendLine($"  {CreatUtil.SetStringField($"{tag}_{method.fixedArg}", method.callArg, order + 3)}");
            }

            builder.AppendLine($"  {CreatUtil.GetFormat(order)}{CreatUtil.RightSymbol}");
        }

        public string GetObsoleteString(PanelData.ArgsData arg , PanelData data)
        {
            if (arg.obsolete == Obsolete.Expired)
            {
                return $"�����ѹ���,��ʹ���µĲ��� => {arg.newFixedArg}";
            }
            else if (arg.obsolete == Obsolete.failure)
            {
                var count  = 10;
                var t_data = arg;
               
                while (string.IsNullOrEmpty(t_data.newFixedArg) == false && count>0)
                {
                    if (TryGetFixed(data, t_data.newFixedArg, out var s_data))
                    {
                        count--;
                        t_data = s_data;
                    }
                    else
                    {
                        break;
                    }
                }
                return $"������ʧЧ => {t_data.newFixedArg}";
            }
            return "";
        }
    }

}


